package top.rainbowecho.gateway.controller;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.ImmutableMap;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import top.rainbowecho.common.exception.BlogException;
import top.rainbowecho.common.mq.config.BlogMqRouteEnum;
import top.rainbowecho.common.mq.config.MqConfiguration;
import top.rainbowecho.common.mq.mo.MailMeta;
import top.rainbowecho.common.mq.mo.SmsMeta;
import top.rainbowecho.common.util.*;
import top.rainbowecho.gateway.security.property.SecurityProperties;
import top.rainbowecho.gateway.service.IUsersService;
import top.rainbowecho.gateway.vo.ValidVo;
import top.rainbowecho.model.field.UsersField;
import top.rainbowecho.model.percona.Users;
import top.rainbowecho.model.view.UserChangePwd;
import top.rainbowecho.model.view.UserRegister;
import top.rainbowecho.model.vo.UserRegisterVo;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 处理用户管理
 * @author rainbow
 * @since 2020/1/26 10:58
 */
@RestController
@Slf4j
@Api("为前端系统提供用户管理相关Api")
public class UsersGatewayController {

    private IUsersService usersService;

    private PasswordEncoder passwordEncoder;

    private StringRedisTemplate redisTemplate;

    private SecurityProperties securityProperties;

    private MqConfiguration mqConfiguration;

    private RabbitTemplate rabbitTemplate;

    @Autowired
    public void setRabbitTemplate(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @Autowired
    public void setMqConfiguration(MqConfiguration mqConfiguration) {
        this.mqConfiguration = mqConfiguration;
    }

    @Autowired
    public void setSecurityProperties(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }

    @Autowired
    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Autowired
    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    @Autowired
    public void setUsersService(IUsersService usersService) {
        this.usersService = usersService;
    }

    @ApiOperation("进行表单注册处理")
    @PostMapping("/form")
    public ResponseEntity<Map<String, Object>> userRegisterByForm(@Validated(UserRegister.class) UserRegisterVo registerVo
            , BindingResult result, HttpServletRequest request) {
        if (result.hasErrors()) {
            return CommonUtil.handleValidateError(result);
        }
        String userIp = CommonUtil.getIpAddress(request);
        // 进行身份证实名校验
        boolean validIdCard = AliApiCallUtil.idCardVerify(registerVo.getIdCard(), registerVo.getRealName());
        if (validIdCard) {
            String encodePassword = this.passwordEncoder.encode(registerVo.getPassword());
            Users formUser = new Users().setUserName(registerVo.getUsername())
                    .setPassword(encodePassword)
                    .setIp(userIp)
                    .setEnable(0)
                    .setEmail(registerVo.getMail())
                    .setPhone(registerVo.getPhone())
                    //.setValidMail(0)
                    //.setValidPhone(0)
                    //.setDeleted(0)
                    //.setRole(PATIENT_ROLE)
                    //.setRealName(registerVo.getRealName())
                    //.setIdCard(registerVo.getIdCard())
                    .setRegisterTime(LocalDateTime.now());

            usersService.save(formUser);
            log.debug("表单注册成功后的User：" + formUser);

            return new ResponseEntity<>(ImmutableMap.of(ResultKey.DATA, formUser), HttpStatus.OK);
        }

        throw new BlogException(ExceptionMessageContent.ID_VERIFY_ERROR);
    }

    @PutMapping("/changepwd")
    @Transactional
    public ResponseEntity<Map<String, Object>> userChangePassword(@RequestBody @Validated(UserChangePwd.class) UserRegisterVo userVo
            , BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return CommonUtil.handleValidateError(bindingResult);
        }
        QueryWrapper<Users> usernameQuery = new QueryWrapper<Users>().eq(UsersField.USER_NAME, userVo.getUsername());
        Users user = this.usersService.getOne(usernameQuery);

        // 如果密码匹配，修改密码
        if (this.passwordEncoder.matches(userVo.getPassword(), user.getPassword())) {
            String encodeNewPassword = this.passwordEncoder.encode(userVo.getNewPassword());
            user.setPassword(encodeNewPassword);
            this.usersService.updateById(user);

            ImmutableMap<String, Object> result = ImmutableMap.of(ResultKey.DATA, user);
            return new ResponseEntity<>(result, HttpStatus.OK);
        }
        throw new BlogException(ExceptionMessageContent.WRONG_PWD);
    }

    /**
     * 提交用户邮箱，申请进行邮箱的验证
     *
     * @return
     */
    @PutMapping("/valid/mail/{id}")
    @Transactional
    public ResponseEntity<Map<String, Object>> applyValidUserMail(@PathVariable("id") String userId
            , @RequestBody ValidVo validVo) {
        String mail = validVo.getMail();
        if (!CommonUtil.matchEmail(mail)) {
            throw new BlogException(ExceptionMessageContent.INVALID_PARAM);
        }

        SecurityProperties.SecurityCodeProperties validCodeProperties = this.securityProperties.getCode();
        Users users = new Users().setId(userId)
                //.setValidMail(0)
                .setEmail(mail);
        this.usersService.updateById(users);

        // 生成6位验证码
        String validCode = RandomUtil.randomString(validCodeProperties.getLength());

        // 存储验证码到Redis，设置过期时间
        String validKey = CommonUtil.generateRedisKey(RedisKey.VALID, RedisKey.MAIL, RedisKey.REGISTER, userId);
        long timeout = validCodeProperties.getTtl().toMinutes();
        this.redisTemplate.opsForValue().set(validKey, validCode, timeout, TimeUnit.MINUTES);

        // 设置参数
        List<String> to = new ArrayList<>();
        to.add(mail);
        ImmutableMap<String, Object> param = ImmutableMap.of(MailKey.CODE, validCode, MailKey.TIMEOUT, timeout);

        // 发送注册验证邮件
        MailMeta mailMeta = new MailMeta(to, MailKey.USER_REGISTER_TEMPLATE, param);
        String routing = this.mqConfiguration.getRoutes().get(BlogMqRouteEnum.GATEWAY_TO_MAIL).getRouting();
        this.rabbitTemplate.convertAndSend(routing, mailMeta);

        ImmutableMap<String, Object> result = ImmutableMap.of(ResultKey.DATA, users);
        return new ResponseEntity<>(result, HttpStatus.OK);
    }

    /**
     * 完成邮箱和手机注册验证后，还需要访问此接口，进行验证确认
     *
     * @param userId
     * @return
     */
    //@PutMapping("/valid/confirm/{id}")
    //@Transactional
    //public ResponseEntity<Map<String, Object>> userValidConfirm(@PathVariable("id") String userId) {
    //    Users userRecord = this.usersService.getById(userId);
    //    boolean validMail = userRecord.getValidMail() == 1;
    //    boolean validPhone = userRecord.getValidPhone() == 1;
    //    if (validMail && validPhone) {
    //        userRecord.setEnable(1);
    //        this.usersService.updateById(userRecord);
    //    } else {
    //        throw new InvalidateParamException(ExceptionMessageContent.NEED_TO_VALID_CONTACT);
    //    }
    //    ImmutableMap<String, Object> result = ImmutableMap.of(ResultKey.DATA, userRecord);
    //    return new ResponseEntity<>(result, HttpStatus.OK);
    //}

    /**
     * 处理邮箱注册验证码
     *
     * @param userId
     * @param validVo
     * @return
     */
    @PutMapping("/handle/mail/{id}")
    @Transactional
    public ResponseEntity<Map<String, Object>> handleUserMailValid(@PathVariable("id") String userId
            , @RequestBody ValidVo validVo) {
        String validCode = validVo.getCode();
        if (StringUtils.isBlank(validCode)) {
            throw new BlogException(ExceptionMessageContent.INVALID_PARAM);
        }

        String validKey = CommonUtil.generateRedisKey(RedisKey.VALID, RedisKey.MAIL, RedisKey.REGISTER, userId);
        String correctValidCode = this.redisTemplate.opsForValue().get(validKey);

        // 验证码过期
        if (!CommonUtil.matchNotBlankStr(correctValidCode)) {
            throw new BlogException(ExceptionMessageContent.CODE_INVALIDATE);
        }

        if (StringUtils.equals(validCode, correctValidCode)) {
            Users users = new Users()
                    //.setValidMail(1)
                    .setId(userId);
            this.usersService.updateById(users);
            ImmutableMap<String, Object> result = ImmutableMap.of(ResultKey.DATA, users);
            return new ResponseEntity<>(result, HttpStatus.OK);
        } else {
            log.debug("提交的验证码为：" + validCode);
            log.debug("Redis中正确的验证码：" + correctValidCode);
            throw new BlogException(ExceptionMessageContent.WRONG_CODE);
        }
    }

    /**
     * 处理
     *
     * @param userId
     * @return
     */
    @PutMapping("/handle/phone/{id}")
    @Transactional
    public ResponseEntity<Map<String, Object>> handleUserPhoneValid(@PathVariable("id") String userId
            , @RequestBody ValidVo validVo) {
        String validCode = validVo.getCode();
        if (StringUtils.isBlank(validCode)) {
            throw new BlogException(ExceptionMessageContent.INVALID_PARAM);
        }

        String validKey = CommonUtil.generateRedisKey(RedisKey.VALID, RedisKey.PHONE, RedisKey.REGISTER, userId);
        String correctValidCode = this.redisTemplate.opsForValue().get(validKey);

        // 验证码过期
        if (!CommonUtil.matchNotBlankStr(correctValidCode)) {
            throw new BlogException(ExceptionMessageContent.CODE_INVALIDATE);
        }

        if (StringUtils.equals(validCode, correctValidCode)) {
            Users users = new Users()
                    //.setValidPhone(1)
                    .setId(userId);
            this.usersService.updateById(users);
            ImmutableMap<String, Object> result = ImmutableMap.of(ResultKey.DATA, users);
            return new ResponseEntity<>(result, HttpStatus.OK);
        } else {
            log.debug("提交的验证码为：" + validCode);
            log.debug("Redis中正确的验证码：" + correctValidCode);
            throw new BlogException(ExceptionMessageContent.WRONG_CODE);
        }
    }

    /**
     * 提交电话，申请进行手机验证码验证
     *
     * @param userId
     * @return
     */
    @PutMapping("/valid/phone/{id}")
    @Transactional
    public ResponseEntity<Map<String, Object>> applyValidUserPhone(@PathVariable("id") String userId
            , @RequestBody ValidVo validVo) {
        String phone = validVo.getPhone();
        if (!CommonUtil.matchMobilePhone(phone)) {
            throw new BlogException(ExceptionMessageContent.INVALID_PARAM);
        }

        SecurityProperties.SecurityCodeProperties validCodeProperties = this.securityProperties.getCode();
        Users users = new Users().setId(userId)
                //.setValidPhone(0)
                .setPhone(phone);
        this.usersService.updateById(users);

        // 生成6位数字验证码
        String validCode = RandomUtil.randomNumbers(validCodeProperties.getLength());

        // 存储验证码到Redis，设置1分钟的过期时间
        String validKey = CommonUtil.generateRedisKey(RedisKey.VALID, RedisKey.PHONE, RedisKey.REGISTER, userId);
        long timeout = validCodeProperties.getTtl().toMinutes();
        this.redisTemplate.opsForValue().set(validKey, validCode, timeout, TimeUnit.MINUTES);

        // 设置参数
        List<String> phoneNumbers = new ArrayList<>();
        phoneNumbers.add(phone);
        ImmutableMap<String, Object> param = ImmutableMap.of(MailKey.CODE, validCode);

        // 发送mq消息到注册短信发送队列
        SmsMeta smsMeta = new SmsMeta(phoneNumbers, param);
        String routing = this.mqConfiguration.getRoutes().get(BlogMqRouteEnum.SMS).getRouting();
        this.rabbitTemplate.convertAndSend(routing, smsMeta);

        ImmutableMap<String, Object> result = ImmutableMap.of(ResultKey.DATA, users);
        return new ResponseEntity<>(result, HttpStatus.OK);
    }
}
